package com.zkjl.protect_business.utils;

import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.word.WordExportUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.zkjl.protect_business.common.BaseException;
import com.zkjl.protect_business.entity.DocField;
import com.zkjl.protect_business.entity.PBFile;
import com.zkjl.protect_business.repository.PBFileRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.model.PicturesTable;
import org.apache.poi.hwpf.usermodel.*;
import org.apache.poi.ss.util.ImageUtils;
import org.apache.poi.util.StringUtil;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.stream.FileImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.DecimalFormat;
import java.util.*;

import static com.zkjl.protect_business.common.Status.IO_ERROR;

/**
 * word 工具类
 */
@Component
@Slf4j
public class WordUtil {

    @Value(value = "${upload.path}")
    private String uploadPath;

    @Autowired
    PBFileRepository pbFileRepository;

    //单元格对像素比例
    final int emu = 9525;

    /**
     * 生成图片
     *
     * @param path
     * @return
     */
    public static ImageEntity createImage(String path, Integer wight, Integer height) {

        byte[] bytes = FileUtil.readBytes(path);
        BufferedImage read = ImgUtil.read(path);
        int height1 = read.getHeight(); //像素高度
        int width = read.getWidth();

        //手动测试比例为16的时候和原图差距最小
        Double emu = DoubleUtils.mul(width, 16);

        //比例
        Double proportion = DoubleUtils.div(wight, emu, 6);
        int finalHeight = DoubleUtils.mul(proportion, height1).intValue();
        int finalWidth = DoubleUtils.mul(width, proportion).intValue();

        ImageEntity imageEntity = new ImageEntity(bytes, finalWidth, finalHeight);

        return imageEntity;
    }




    /**
     * 导出word
     * <p>第一步生成替换后的word文件，只支持docx</p>
     * <p>第二步下载生成的文件</p>
     * <p>第三步删除生成的临时文件</p>
     * 模版变量中变量格式：{{foo}}
     *
     * @param templatePath word模板地址
     * @param temDir       生成临时文件存放地址
     * @param fileName     文件名
     * @param params       替换的参数
     */
    public static File exportWord(String templatePath, String temDir, String fileName, Map<String, Object> params) {
        String tmpPath="";
        Assert.notNull(templatePath, "模板路径不能为空");
        Assert.notNull(temDir, "临时文件路径不能为空");
        Assert.notNull(fileName, "导出文件名不能为空");
        Assert.isTrue(fileName.endsWith(".docx"), "word导出请使用docx格式");
        if (!temDir.endsWith("/")) {
            temDir = temDir + File.separator;
        }
        File dir = new File(temDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        //复制模板到新路径
        tmpPath = temDir + fileName;
        try {
            toDealWithNull(params);
            XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params);
            FileOutputStream fos = new FileOutputStream(tmpPath);
            doc.write(fos);
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new File(tmpPath);
    }


    /**
     * 处理空值
     * @param params
     */
    public static void toDealWithNull(Map<String, Object> params) {

        Set<Map.Entry<String, Object>> entries = params.entrySet();
        for (Map.Entry<String, Object> entry : entries) {

            Object value = entry.getValue();
            if (value == null) {
                entry.setValue("");
            }
        }

    }


    /**
     * 解析word文档中包含的字段
     * @param multipartFile
     * @return
     * @throws IOException
     */
    public static List<DocField> parsingField(MultipartFile multipartFile) throws IOException {

        List<DocField> result = new ArrayList<>();

        InputStream in = multipartFile.getInputStream();

        String originalFilename = multipartFile.getOriginalFilename();

        if (originalFilename.endsWith(".doc")) {

            result = parsingDocField(in);

        } else if (originalFilename.endsWith(".docx")) {

            result = parsingDocxField(in);
        }

        return result;
    }

    /**
     * 解析word文档
     * @param file
     * @param uploadPath
     * @return
     * @throws IOException
     */
    public Map<String,Object> parsing(File file, String uploadPath) throws IOException {

        Map<String, Object> result = new HashMap<>();

        BufferedInputStream inputStream = FileUtil.getInputStream(file);

        String originalFilename = file.getName();

        try {
            if (originalFilename.endsWith(".doc")) {

                result = parsingDoc(inputStream, uploadPath);

            } else if (originalFilename.endsWith(".docx")) {

                result = parsingDocx(inputStream, uploadPath);
            }
        } catch (Exception e) {
            log.error("解析异常", e);
            throw new BaseException(IO_ERROR.getCode(), "文件格式可能已损坏，请确认后重试！", e.getMessage());
        }

        return result;

    }




    public Map<String,Object> parsingDocx(MultipartFile multipartFile, String uploadPath) throws IOException {
        InputStream in = multipartFile.getInputStream();
        Map<String, Object> map = parsingDocx(in, uploadPath);
        return map;
    }


    public Map<String, Object> parsingDocx(InputStream in, String uploadPath) throws IOException {

        Map<String, Object> map = new HashMap<>();

        //word 2007 图片不会被读取， 表格中的数据会被放在字符串的最后
        XWPFDocument xwpf = new XWPFDocument(in);//得到word文档的信息
//             List<XWPFParagraph> listParagraphs = xwpf.getParagraphs();//得到段落信息
        Iterator<XWPFTable> it = xwpf.getTablesIterator();//得到word中的表格


        while (it.hasNext()) {

            XWPFTable table = it.next();
            List<XWPFTableRow> rows = table.getRows();
            //读取每一行数据
            for (int i = 0; i < rows.size(); i++) {
                XWPFTableRow row = rows.get(i);
                //读取每一列数据
                List<XWPFTableCell> cells = row.getTableCells();
                for (int j = 0; j < cells.size(); j++) {

                    String location = i + "," + j;
                    XWPFTableCell cell = cells.get(j);

                    String text = cell.getText();
                    if (StringUtils.isBlank(text)) {
                        text = getCellImage(cell, uploadPath);
                    }

                    text = text.trim();
                    map.put(location, text);

                }
            }
        }

        return map;
    }


    public String getCellImage(XWPFTableCell cell, String uploadPath) {

        List<XWPFParagraph> xwpfParagraphs = cell.getParagraphs();
        if (xwpfParagraphs == null) return null;
        for (XWPFParagraph xwpfParagraph : xwpfParagraphs) {
            List<XWPFRun> xwpfRunList = xwpfParagraph.getRuns();
            if (xwpfRunList == null) return null;
            for (XWPFRun xwpfRun : xwpfRunList) {
                List<XWPFPicture> xwpfPictureList = xwpfRun.getEmbeddedPictures();
                if (xwpfParagraph == null) return null;
                for (XWPFPicture xwpfPicture : xwpfPictureList) {
                    byte[] data = xwpfPicture.getPictureData().getData();
                    String fileName = xwpfPicture.getPictureData().getFileName();
                    String simpleUUID = IdUtil.simpleUUID();

                    String fileType = getFileType(fileName);

                    File uploadDirectory = new File(uploadPath);
                    if (uploadDirectory.exists()) {
                        if (!uploadDirectory.isDirectory()) {
                            uploadDirectory.delete();
                        }
                    } else {
                        uploadDirectory.mkdir();
                    }

                    String path = uploadPath + "/" + simpleUUID + fileType;
                    String hash = MD5Util.digestHex(data);
                    //验证文件是否已上传
                    PBFile byHash = pbFileRepository.findByHash(hash);
                    if (byHash != null) {
                        String name = byHash.getName();
                        return "/files/" + name;
                    }

                    PBFile pbFile = new PBFile();
                    pbFile.setId(simpleUUID);
                    pbFile.setBaseInfo();
                    pbFile.setName(simpleUUID + fileType);
                    pbFile.setPath(path);
                    pbFile.setHash(hash);

                    pbFileRepository.save(pbFile);

                    byte2image(data,path );
                    return "/files/" + simpleUUID + fileType;
                }

            }
        }
        return "";
    }

    public static  void byte2image(byte[] data,String path){
        if(data.length<3||path.equals("")) return;
        FileImageOutputStream imageOutput = null;
        try{


            imageOutput = new FileImageOutputStream(new File(path));
            imageOutput.write(data, 0, data.length);
            System.out.println("Make Picture success,Please find image in " + path);
        } catch(Exception ex) {
            System.out.println("Exception: " + ex);
            ex.printStackTrace();
        }finally {
            try {
                imageOutput.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 解析doc文档
     * @param multipartFile
     * @return
     * @throws IOException
     */
    public Map<String,Object> parsingDoc(MultipartFile multipartFile, String uploadPath) throws IOException {
        InputStream in = multipartFile.getInputStream();
        Map<String, Object> map = parsingDoc(in, uploadPath);
        return map;
    }


    /**
     * 解析doc文档
     *
     * @param in
     * @return
     * @throws IOException
     */
    public Map<String, Object> parsingDoc(InputStream in, String uploadPath) throws IOException {

        Map<String, Object> map = new HashMap<>();

        HWPFDocument doc = new HWPFDocument(in);
        List<DocField> result = new ArrayList<>();
        Range range = doc.getRange();//得到文档的读取范围
        TableIterator it = new TableIterator(range);

        PicturesTable pTable = doc.getPicturesTable(); //文档中的图片


        //迭代文档中的表格
        while (it.hasNext()) {

            it.hasNext();
            Table tb = (Table) it.next();
            //迭代行，默认从0开始
            for (int i = 0; i < tb.numRows(); i++) {
                TableRow tr = tb.getRow(i);
                //迭代列，默认从0开始
                for (int j = 0; j < tr.numCells(); j++) {
                    TableCell td = tr.getCell(j);//取得单元格
                    String location = i + "," + j;

                    CharacterRun cr = td.getCharacterRun(0);
                    //处理图片
                    if (pTable.hasPicture(cr)) {
                        Picture pic = pTable.extractPicture(cr, false);
                        String afileName = pic.suggestFullFileName();
                        String simpleUUID = IdUtil.simpleUUID();
                        File uploadDirectory = new File(uploadPath);
                        if (uploadDirectory.exists()) {
                            if (!uploadDirectory.isDirectory()) {
                                uploadDirectory.delete();
                            }
                        } else {
                            uploadDirectory.mkdir();
                        }
                        String path = uploadPath + "/" + simpleUUID + getFileType(afileName);

                        OutputStream out = new FileOutputStream(new File(path));
                        pic.writeImageContent(out);
                        out.close();

                        //验证图片是否已上传
                        File file = FileUtil.file(path);
                        String hash = MD5Util.digestHex(file);

                        PBFile byHash = pbFileRepository.findByHash(hash);
                        if (byHash != null) {
                            String name = byHash.getName();
                            map.put(location, "/files/" + name);
                            continue;
                        }

                        PBFile pbFile = new PBFile();
                        pbFile.setId(simpleUUID);
                        pbFile.setBaseInfo();
                        pbFile.setName(simpleUUID + getFileType(afileName));
                        pbFile.setPath(path);
                        pbFile.setHash(hash);

                        pbFileRepository.save(pbFile);


                        map.put(location, "/files/" + simpleUUID + "/" + afileName);
                    }else {
                        //取得单元格的内容
                        String text = td.text();
                        text = text.trim();

                        map.put(location, text);
                    }


                }   //end for
            }   //end for
        } //end while
        return map;

    }

    /**
     * 获取文件后缀名
     *
     * @param fileName
     * @return
     */
    private String getFileType(String fileName) {
        if (fileName != null && fileName.indexOf(".") >= 0) {
            return fileName.substring(fileName.lastIndexOf("."), fileName.length());
        }
        return "";
    }


    /**
     * 解析doc文档里面包含的字段
     * @param in
     * @return
     * @throws IOException
     */
    public static List<DocField> parsingDocField(InputStream in) throws IOException {

        HWPFDocument doc = new HWPFDocument(in);
        List<DocField> result = new ArrayList<>();
        Range range = doc.getRange();//得到文档的读取范围
        TableIterator it = new TableIterator(range);
        //迭代文档中的表格
        while (it.hasNext()) {
            Table tb = (Table) it.next();
            //迭代行，默认从0开始
            for (int i = 0; i < tb.numRows(); i++) {
                TableRow tr = tb.getRow(i);
                //迭代列，默认从0开始
                for (int j = 0; j < tr.numCells(); j++) {
                    TableCell td = tr.getCell(j);//取得单元格
                    int rowHeight = tr.getRowHeight();  //高度
                    int width = td.getWidth();  //宽度

                    String location = i + "," + j;

                    //取得单元格的内容
                    for (int k = 0; k < td.numParagraphs(); k++) {

                        Paragraph para = td.getParagraph(k);

                        String text = para.text();
                        text = text.trim();
                        if (text.startsWith("{{") && text.endsWith("}}")) {
                            DocField docField = new DocField();
                            String replace = text.replace("{{", "");
                            String replace1 = replace.replace("}}", "");
                            docField.setKey(replace1);
                            docField.setLocation(location);
                            docField.setCellHeight(rowHeight);
                            docField.setCellWidth(width);

                            result.add(docField);
                            //输出当前的单元格的数据
                            System.out.println(text);
                        }

                    } //end for
                }   //end for
            }   //end for
        } //end while
        return result;
    }


    /**
     * 解析Docx文档里面包含的字段
     * @param in
     * @return
     * @throws IOException
     */
    public static List<DocField> parsingDocxField(InputStream in) throws IOException {

        List<DocField> result = new ArrayList<>();

        //word 2007 图片不会被读取， 表格中的数据会被放在字符串的最后
        XWPFDocument xwpf = new XWPFDocument(in);//得到word文档的信息
//             List<XWPFParagraph> listParagraphs = xwpf.getParagraphs();//得到段落信息
        Iterator<XWPFTable> it = xwpf.getTablesIterator();//得到word中的表格

        while(it.hasNext()){

            XWPFTable table = it.next();
            List<XWPFTableRow> rows=table.getRows();
            //读取每一行数据
            for (int i = 0; i < rows.size(); i++) {
                XWPFTableRow  row = rows.get(i);
                //https://www.hotbak.net/key/poi%E8%AE%BE%E7%BD%AE%E8%A1%8C%E9%AB%98.html
                //1px = 0.75pt 1px = 15twips
                int height = row.getHeight();   //高度

                //读取每一列数据
                List<XWPFTableCell> cells = row.getTableCells();
                for (int j = 0; j < cells.size(); j++) {

                    String location = i + "," + j;
                    XWPFTableCell cell = cells.get(j);
                    int width = cell.getWidth();    //宽度

                    String text = cell.getText();
                    text = text.trim();
                    if (text.startsWith("{{") && text.endsWith("}}")) {
                        DocField docField = new DocField();
                        String replace = text.replace("{{", "");
                        String replace1 = replace.replace("}}", "");
                        docField.setKey(replace1);
                        docField.setLocation(location);
                        docField.setCellHeight(height);
                        docField.setCellWidth(width);

                        result.add(docField);
                        //输出当前的单元格的数据
                        System.out.println(cell.getText());
                    }


                }
            }
        }

        return result;
    }



}
